package com.ztesoft.book.api.config.shiro.handler;

import com.ztesoft.book.common.utils.context.AopTargetUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
import org.apache.shiro.subject.Subject;

/**
 * @Description 自定义权限拦截器
 * @Author YangFan
 * @Date 2021-07-07 23:50
 */
@Slf4j
public class MyPermissionAnnotationHandler extends PermissionAnnotationHandler {

    /**
     * 方法功能描述:
     * < 授权断言 >
     *
     * @param mi 【参数 1 注释说明】
     * @return void                            【返回值 注释说明】
     * @author YangFan
     * @date 2021-07-07 23:50
     */
    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
        RequiresPermissions methodAnnotation = mi.getMethod().getAnnotation(RequiresPermissions.class);
        if (methodAnnotation != null) {
            String[] methodPerms = methodAnnotation.value();
            Subject subject = getSubject();
            if (methodPerms.length == 1) {
                handleLength1(mi, methodPerms, subject);
                return;
            }
            if (Logical.AND.equals(methodAnnotation.logical())) {
                getSubject().checkPermissions(methodPerms);
                return;
            }
            if (Logical.OR.equals(methodAnnotation.logical())) {
                boolean hasAtLeastOnePermission = false;
                for (String permission : methodPerms) {
                    if (getSubject().isPermitted(permission)) {
                        hasAtLeastOnePermission = true;
                    }
                }
                if (!hasAtLeastOnePermission) {
                    getSubject().checkPermission(methodPerms[0]);
                    getSubject().checkPermissions();
                }
            }
        }
    }

    /**
     * 方法功能描述:
     * < 自定义实现类上的注解拦截 >
     *
     * @param mi          【参数 1 注释说明】
     * @param methodPerms 【参数 2 注释说明】
     * @param subject     【参数 3 注释说明】
     * @return void                            【返回值 注释说明】
     * @author YangFan
     * @date 2021-07-07 23:51
     */
    private void handleLength1(MethodInvocation mi, String[] methodPerms, Subject subject) {
        RequiresPermissions classAnnotation = null;
        // 这里一开始获取不到类上的注解，因为这里获取的class是cglib代理类（不携带任何注解），所以通过aop工具类获取真实的类才能获取类上的权限注解
        try {
            classAnnotation = AopTargetUtils.getTarget(mi.getThis()).getClass().getAnnotation(RequiresPermissions.class);
        }
        catch (Exception e) {
            log.error("handleLength1", e);
        }
        if (null != classAnnotation) {
            String[] classPerms = classAnnotation.value();
            subject.checkPermission(classPerms[0] + methodPerms[0]);
        }
        else {
            subject.checkPermission(methodPerms[0]);
        }
    }
}
